/*
 * @Author: Wangjun
 * @Date: 2021-06-11 18:00:32
 * @LastEditTime: 2022-09-21 15:23:50
 * @LastEditors: wangjun haodreams@163.com
 * @Description:生成code
 * @FilePath: \rtserviced:\go\src\gitee.com\haodreams\golib\gensql\gensql.go
 * hnxr
 */
package gensql

import (
	"database/sql/driver"
	"errors"
	"reflect"
	"strings"

	"gorm.io/gorm"
	"gorm.io/gorm/callbacks"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/schema"
)

func GenSQLData(xdb *gorm.DB, array interface{}) (sql string, rows [][]interface{}, err error) {
	v := reflect.ValueOf(array)
	if v.Type().Kind() != reflect.Slice {
		err = errors.New("params must be array or slice")
		return
	}
	num := v.Len()
	if num == 0 {
		err = errors.New("no data")
		return
	}
	vv := v.Index(0)
	o := vv.Interface()
	db := xdb.Model(o)
	stmt := db.Statement
	stmt.Model = o
	stmt.Dest = stmt.Model
	stmt.BuildClauses = []string{"INSERT", "VALUES"}
	stmt.SQL.Grow(180)
	stmt.AddClauseIfNotExists(clause.Insert{})

	stmt.Model = o
	stmt.Parse(o)
	for i := 0; i < num; i++ {
		vv := v.Index(i)
		o := vv.Interface()

		stmt.Dest = o
		stmt.ReflectValue = reflect.ValueOf(stmt.Dest)
		for stmt.ReflectValue.Kind() == reflect.Ptr {
			if stmt.ReflectValue.IsNil() && stmt.ReflectValue.CanAddr() {
				stmt.ReflectValue.Set(reflect.New(stmt.ReflectValue.Type().Elem()))
			}
			stmt.ReflectValue = stmt.ReflectValue.Elem()
		}
		if !stmt.ReflectValue.IsValid() {
			err = gorm.ErrInvalidValue
			return
		}
		stmt.SQL.Reset()
		stmt.Vars = nil

		//此处可以优化 callbacks.ConvertToCreateValues(stmt) 已经返回了值
		//可以不使用 build，待测试
		stmt.AddClause(callbacks.ConvertToCreateValues(stmt))
		if i == 0 {
			if tabler, ok := o.(schema.Tabler); ok {
				stmt.Table = tabler.TableName()
			}

			stmt.Build(stmt.BuildClauses...)
			sql = stmt.SQL.String()
		} else {
			if c, ok := stmt.Clauses["VALUES"]; ok {
				c.Build(stmt)
			}
		}
		rows = append(rows, stmt.Vars)
	}
	return
}

type Valuer interface {
	Value() (v driver.Value, err error)
}

type Tabler interface {
	TableName() string
}

// GetStructValues 获取设备字段对应的值
func GetStructValues(items interface{}) (values []interface{}) {
	v := reflect.ValueOf(items)
	if v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	} else if v.Kind() != reflect.Struct {
		return
	}

	//获取 device 的每个字段
	values = make([]interface{}, v.NumField())
	idx := 0
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		typField := field.Type()
		//logs.Info(typField.String())
		//找到每个Item字段
		if typField.Kind() == reflect.Struct { //reflect.Ptr {
			fd := field.Addr().Interface()
			if vs, ok := fd.(Valuer); ok {
				values[idx], _ = vs.Value()
			} else {
				values[idx] = nil
			}
			idx++
		} else if typField.Kind() == reflect.String {
			if field.CanInterface() {
				values[idx] = field.Interface()
				idx++
			}
		} else {
			values[idx] = field.Interface()
			idx++
		}
	}

	return values[:idx]
}

// GetTitlesFromStruct 获取类中的成员变量和变量对应的值
func GetTitlesFromStruct(class interface{}) (name string, titles []string) {
	v := reflect.ValueOf(class)
	if v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	} else if v.Kind() != reflect.Struct {
		return
	}

	t := v.Type()

	if tc, ok := class.(Tabler); ok {
		name = tc.TableName()
		if name == "" {
			name = t.Name()
		}
	} else {
		name = t.Name()
	}

	for i := 0; i < v.NumField(); i++ {
		fieldType := t.Field(i)
		c := fieldType.Name[0]
		if c >= 'a' && c <= 'z' {
			continue
		}

		column := fieldType.Tag.Get("gorm")
		ss := strings.Split(column, " ")
		if strings.HasPrefix(ss[0], "column:") {
			column = strings.TrimPrefix(ss[0], "column:")
		} else {
			column = fieldType.Name
		}
		titles = append(titles, column)
	}

	return
}

/**
 * @description: 成员变量和ID对应, 只查找最顶级的结构体变量
 * @param {interface{}} class
 * @return {*}
 */
func GetMapFieldFromStruct(class interface{}) (mp map[string]int) {
	mp = map[string]int{}
	v := reflect.ValueOf(class)
	if v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	} else if v.Kind() != reflect.Struct {
		return
	}

	t := v.Type()

	for i := 0; i < v.NumField(); i++ {
		fieldType := t.Field(i)
		if fieldType.Name == "" {
			continue
		}
		c := fieldType.Name[0]
		if c >= 'a' && c <= 'z' {
			continue
		}
		mp[fieldType.Name] = i
	}

	return
}
